using System;
using System.IO;
using System.Globalization;
using System.Drawing;

namespace UGent.Tsp
{
	public class MapVisualizer
	{
	    private const int Radius = 3;
	    
	    public MapVisualizer(Route route)
	    {
	        if(route == null)
	            throw new ArgumentNullException("map");
	        
	        this.route = route;
	        this.map = route.Map;
	        
	        if(map.Dimension != 2 || map.Metric != Metric.Euclidian)
	            throw new ArgumentException("This map cannot be drawn.", "map");
	    }
	    
	    private bool drawRoute = true;
	    public bool DrawRoute
	    {
	        get { return drawRoute; }
	        set { drawRoute = value; }
	    }
	    
	    private Route route;
	    public Route Route
	    {
	        get { return route; }
	    }
	    
	    private Map map;
	    public Map Map
	    {
	        get { return map; }
	    }
	    
	    int width;
	    int height;
	    
	    public virtual void PaintMap(Image image)
	    {
	        // Allow a border of size Radius to make sure that the
            // dots are displayed correctly.
	        width = image.Width - 2 * Radius;
	        height = image.Height - 2 * Radius;
	        
	        using(Graphics graphics = Graphics.FromImage(image))
	        {
                for (int city = 0; city < Map.CityCount; city++)
                {
                    Point location = GetLocation(city, true);
                    location.Offset(-1 * Radius, -1 * Radius);
                    Size size = new Size(2 * Radius, 2 * Radius);

                    Rectangle circle = new Rectangle(location, size);

                    graphics.DrawEllipse(Pens.Green, circle);
                    graphics.FillEllipse(Brushes.Green, circle);
                }

                if (DrawRoute)
                {
                    for (int city = 0; city < Map.CityCount; city++)
                    {
                        Point current = GetLocation(Route[city], true);
                        Point next = GetLocation(Route[city + 1], true);

                        graphics.DrawLine(Pens.Red, current, next);
                    }
                }	            
	        }
	    }

        public virtual void MapToLatex(Stream stream, int rescaleFactor)
        {
            width = 200;
            height = 200;

            using(StreamWriter writer = new StreamWriter(stream))
            {
                writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "\\begin{{pspicture}}({0},{1})", (double)width/20.0/(double)rescaleFactor, (double)height/20.0/(double)rescaleFactor));

                for(int city = 0; city < Map.CityCount; city++)
                {
                    Point location = GetLocation(city, false);
                    writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "\\psdot({0},{1})", (double)location.X/20.0/(double)rescaleFactor, (double)location.Y/20.0/(double)rescaleFactor));
                }
                
                if (DrawRoute)
                {
                    for (int city = 0; city < Map.CityCount; city++)
                    {
                        Point current = GetLocation(Route[city], false);
                        Point next = GetLocation(Route[city + 1], false);

                        writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "\\psline({0},{1})({2},{3})", (double)current.X/20.0/(double)rescaleFactor, (double)current.Y/20.0/(double)rescaleFactor, (double)next.X/20.0/(double)rescaleFactor, (double)next.Y/20.0/(double)rescaleFactor));
                    }
                }	
                
                writer.WriteLine("\\end{pspicture}");
            }
        }

        private Point GetLocation(int city,  bool invertY)
        {
            double mapWidth = Map.GetLength(0);
            double mapHeight = Map.GetLength(1);

            double xOffset = Map.GetOffset(0);
            double yOffset = Map.GetOffset(1);

            // Renormalize the coordinates to [0,1]
            double x = (Map.GetCoordinate(city, 0) - xOffset) / mapWidth;
            double y = (Map.GetCoordinate(city, 1) - yOffset) / mapHeight;

			// y coordinate is increasing from top to bottom
			// http://msdn2.microsoft.com/en-us/library/ms229598.aspx
            if(invertY)
            {
    			y = 1.0 - y;
			}

            int xPos = (int)(x * (double)width) + Radius;
            int yPos = (int)(y * (double)height) + Radius;

            return new Point(xPos, yPos);
        }
        
        public static void RunOnConsole()
        {
            Console.WriteLine("Enter the name of the map you wish to visualize");
            string line = Console.ReadLine();
            Map map = new Map(line);
            
            Console.WriteLine("Enter a rescale factor, if desired. (2 == half the size. ENTER for none)");
            line = Console.ReadLine();
            int rescaleFactor = 1;
            if(!string.IsNullOrEmpty(line))
                rescaleFactor = int.Parse(line);
                
            
            Console.WriteLine("Enter the name of the route you wish to visualize (ENTER for none)");
            line = Console.ReadLine();
            Route route = new Route(map);
            bool drawRoute = !string.IsNullOrEmpty(line);
            
            if(drawRoute)
            {
                using(StreamReader reader = new StreamReader(line))
                {
                    while(reader.Peek() >= 0)
                    {
                        line = reader.ReadLine();
                        string[] parts = line.Split(new char[]{','});
                        int index = int.Parse(parts[0]);
                        int value = int.Parse(parts[1]);
                        route[index] = value;
                    }
                }
            }
            
            Console.WriteLine("Route length is {0}.", route.GetLength());
            
            MapVisualizer visualizer = new MapVisualizer(route);
            visualizer.DrawRoute = drawRoute;
            Bitmap bitmap = new Bitmap(600, 600);
            visualizer.PaintMap(bitmap);
            using(FileStream stream = new FileStream("route.tex", FileMode.Create))
            {
                visualizer.MapToLatex(stream, rescaleFactor);
            }
            bitmap.Save("route.bmp");
        }
	}
}
